package tourguide.tourguide; import android.animation.AnimatorSet; import android.app.Activity; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Path; import android.graphics.Path.FillType; import android.support.v4.view.MotionEventCompat; import android.text.TextPaint; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.animation.Animation; import android.widget.FrameLayout; import java.util.ArrayList; /** * TODO: document your custom view class. */ public class FrameLayoutWithHole extends FrameLayout { private TextPaint mTextPaint; private TourGuide.MotionType mMotionType; private Path mPath; private Paint mPaint; private View mViewHole; // This is the targeted view to be highlighted, where the hole should be placed private int mRadius; private int [] mPos; private float mDensity; private Overlay mOverlay; private ArrayList<AnimatorSet> mAnimatorSetArrayList; public void setViewHole(View viewHole) { this.mViewHole = viewHole; enforceMotionType(); } public void addAnimatorSet(AnimatorSet animatorSet){ if (mAnimatorSetArrayList==null){ mAnimatorSetArrayList = new ArrayList<AnimatorSet>(); } mAnimatorSetArrayList.add(animatorSet); } private void enforceMotionType(){ Log.d("tourguide", "enforceMotionType 1"); if (mViewHole!=null) {Log.d("tourguide","enforceMotionType 2"); if (mMotionType!=null && mMotionType == TourGuide.MotionType.ClickOnly) { Log.d("tourguide","enforceMotionType 3"); Log.d("tourguide","only Clicking"); mViewHole.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View view, MotionEvent motionEvent) { mViewHole.getParent().requestDisallowInterceptTouchEvent(true); return false; } }); } else if (mMotionType!=null && mMotionType == TourGuide.MotionType.SwipeOnly) { Log.d("tourguide","enforceMotionType 4"); Log.d("tourguide","only Swiping"); mViewHole.setClickable(false); } } } public FrameLayoutWithHole(Activity context, View view) { this(context, view, TourGuide.MotionType.AllowAll); } public FrameLayoutWithHole(Activity context, View view, TourGuide.MotionType motionType) { this(context, view, motionType, new Overlay()); } public FrameLayoutWithHole(Activity context, View view, TourGuide.MotionType motionType, Overlay overlay) { super(context); mViewHole = view; init(null, 0); enforceMotionType(); mOverlay = overlay; int [] pos = new int[2]; mViewHole.getLocationOnScreen(pos); mPos = pos; mDensity = context.getResources().getDisplayMetrics().density; int padding = (int)(20 * mDensity); if (mViewHole.getHeight() > mViewHole.getWidth()) { mRadius = mViewHole.getHeight()/2 + padding; } else { mRadius = mViewHole.getWidth()/2 + padding; } mMotionType = motionType; } private void init(AttributeSet attrs, int defStyle) { // Load attributes // final TypedArray a = getContext().obtainStyledAttributes( // attrs, FrameLayoutWithHole, defStyle, 0); // // // a.recycle(); setWillNotDraw(false); // Set up a default TextPaint object mTextPaint = new TextPaint(); mTextPaint.setFlags(Paint.ANTI_ALIAS_FLAG); mTextPaint.setTextAlign(Paint.Align.LEFT); mPath = new Path(); mPaint = new Paint(); mPaint.setAntiAlias(true); } private boolean mCleanUpLock = false; protected void cleanUp(){ if (getParent() != null) { if (mOverlay!=null && mOverlay.mExitAnimation!=null) { performOverlayExitAnimation(); } else { ((ViewGroup) this.getParent()).removeView(this); } } } private void performOverlayExitAnimation(){ if (!mCleanUpLock) { final FrameLayout _pointerToFrameLayout = this; mCleanUpLock = true; Log.d("tourguide","Overlay exit animation listener is overwritten..."); mOverlay.mExitAnimation.setAnimationListener(new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) {} @Override public void onAnimationRepeat(Animation animation) {} @Override public void onAnimationEnd(Animation animation) { ((ViewGroup) _pointerToFrameLayout.getParent()).removeView(_pointerToFrameLayout); } }); this.startAnimation(mOverlay.mExitAnimation); } } /* comment this whole method to cause a memory leak */ @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); if (mAnimatorSetArrayList != null && mAnimatorSetArrayList.size() > 0){ for(int i=0;i<mAnimatorSetArrayList.size();i++){ mAnimatorSetArrayList.get(i).removeAllListeners(); mAnimatorSetArrayList.get(i).end(); } } } /** Show an event in the LogCat view, for debugging */ private void dumpEvent(MotionEvent event) { String names[] = { "DOWN" , "UP" , "MOVE" , "CANCEL" , "OUTSIDE" , "POINTER_DOWN" , "POINTER_UP" , "7?" , "8?" , "9?" }; StringBuilder sb = new StringBuilder(); int action = event.getAction(); int actionCode = action & MotionEvent.ACTION_MASK; sb.append("event ACTION_" ).append(names[actionCode]); if (actionCode == MotionEvent.ACTION_POINTER_DOWN || actionCode == MotionEvent.ACTION_POINTER_UP) { sb.append("(pid " ).append( action >> MotionEvent.ACTION_POINTER_ID_SHIFT); sb.append(")" ); } sb.append("[" ); for (int i = 0; i < event.getPointerCount(); i++) { sb.append("#" ).append(i); sb.append("(pid " ).append(event.getPointerId(i)); sb.append(")=" ).append((int) event.getX(i)); sb.append("," ).append((int) event.getY(i)); if (i + 1 < event.getPointerCount()) sb.append(";" ); } sb.append("]" ); Log.d("tourguide", sb.toString()); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { //first check if the location button should handle the touch event dumpEvent(ev); int action = MotionEventCompat.getActionMasked(ev); if(mViewHole != null) { int[] pos = new int[2]; mViewHole.getLocationOnScreen(pos); Log.d("tourguide", "[dispatchTouchEvent] mViewHole.getHeight(): "+mViewHole.getHeight()); Log.d("tourguide", "[dispatchTouchEvent] mViewHole.getWidth(): "+mViewHole.getWidth()); Log.d("tourguide", "[dispatchTouchEvent] Touch X(): "+ev.getRawX()); Log.d("tourguide", "[dispatchTouchEvent] Touch Y(): "+ev.getRawY()); // Log.d("tourguide", "[dispatchTouchEvent] X of image: "+pos[0]); // Log.d("tourguide", "[dispatchTouchEvent] Y of image: "+pos[1]); Log.d("tourguide", "[dispatchTouchEvent] X lower bound: "+ pos[0]); Log.d("tourguide", "[dispatchTouchEvent] X higher bound: "+(pos[0] +mViewHole.getWidth())); Log.d("tourguide", "[dispatchTouchEvent] Y lower bound: "+ pos[1]); Log.d("tourguide", "[dispatchTouchEvent] Y higher bound: "+(pos[1] +mViewHole.getHeight())); if(ev.getRawY() >= pos[1] && ev.getRawY() <= (pos[1] + mViewHole.getHeight()) && ev.getRawX() >= pos[0] && ev.getRawX() <= (pos[0] + mViewHole.getWidth())) { //location button event Log.d("tourguide","to the BOTTOM!"); Log.d("tourguide",""+ev.getAction()); // switch(action) { // case (MotionEvent.ACTION_DOWN) : // Log.d("tourguide","Action was DOWN"); // return false; // case (MotionEvent.ACTION_MOVE) : // Log.d("tourguide","Action was MOVE"); // return true; // case (MotionEvent.ACTION_UP) : // Log.d("tourguide","Action was UP"); //// ev.setAction(MotionEvent.ACTION_DOWN|MotionEvent.ACTION_UP); //// return super.dispatchTouchEvent(ev); // return false; // case (MotionEvent.ACTION_CANCEL) : // Log.d("tourguide","Action was CANCEL"); // return true; // case (MotionEvent.ACTION_OUTSIDE) : // Log.d("tourguide","Movement occurred outside bounds " + // "of current screen element"); // return true; // default : // return super.dispatchTouchEvent(ev); // } // return mViewHole.onTouchEvent(ev); return false; } } return super.dispatchTouchEvent(ev); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (mOverlay != null) { mPath.rewind(); mPath.addRect(0, 0, canvas.getWidth(), canvas.getHeight(), Path.Direction.CW); if (mOverlay.mStyle == Overlay.Style.Rectangle) { int padding = (int) (10 * mDensity); mPath.addRect(mPos[0] - padding, mPos[1] - padding, mPos[0] + mViewHole.getWidth() + padding, mPos[1] + mViewHole.getHeight() + padding, Path.Direction.CCW); } else { mPath.addCircle(mPos[0] + mViewHole.getWidth() / 2, mPos[1] + mViewHole.getHeight() / 2, mRadius, Path.Direction.CCW); } mPath.setFillType(FillType.WINDING); mPaint.setColor(mOverlay.mBackgroundColor); canvas.drawPath(mPath, mPaint); } } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); if (mOverlay!=null && mOverlay.mEnterAnimation!=null) { this.startAnimation(mOverlay.mEnterAnimation); } } /** * * Convenient method to obtain screen width in pixel * * @param activity * @return screen width in pixel */ public int getScreenWidth(Activity activity){ return activity.getResources().getDisplayMetrics().widthPixels; } /** * * Convenient method to obtain screen height in pixel * * @param activity * @return screen width in pixel */ public int getScreenHeight(Activity activity){ return activity.getResources().getDisplayMetrics().heightPixels; } }